/* 
    封装ajax
    每发一个请求都需要用ajax请求的4个步骤
    参数：可变的数据 就可以当成参数去写
        url: 请求的url地址  必填
        type: 请求的方式 get post 选填，默认值get
        data：请求的携带的参数（可有可无） 选填，默认值 ''
        async：请求是否异步 （true || false） 选填，默认值为true 表示异步
        success:function(){} 请求成功的回调函数（获取请求结果的函数） 必填，需要获取结果
        error:function(){} 请求失败的回调函数 选填

    因为有写参数是可选，如果一个参数一个参数传递 当有些参数不传递的时候会导致参数错乱的情况
    所以把参数都写在一个对象中进行传递
*/

function ajax(option) {
    /* 
        参数的判断
            【1】判断必填的参数是否有填写
            【2】默认值的编写
            【3】type：必须是get 或者 post
            【4】data:是否是字符串的形式 和 对象{username：'aaa',age:18}
            【5】async： 取值只能是布尔值
            【6】回调函数：当请求成功的时候去调用的一个函数,判断success 一定要为一个函数
    */
    // 判断错误的时候 然后就终止代码不在执行
    if (!option.url) {
        // console.log("url地址是必填");
        // return;
        //手动抛出异常 错误
        // 当执行这条代码之后 那么这条代码之后的代码也不会执行
        throw new Error("url地址必填");
    }

    if (!option.success) {
        throw new Error("success回调函数是必填");
    }

    // 默认值的编写：
    let params = {
        url: option.url,
        type: option.type || "get", // 如果option.type有值 就把option.type赋值给type属性，否则就把'get' 赋值给type
        data: option.data || "",
        // async:option.async || true; 无论option.async 是否true 或者false 最终的结果都会得到true
        // 如果option.async == false 就把 false 赋值给async 否则就就给async赋值true
        async: option.async == false ? false : true,
        success: option.success,
        error: option.error || function () {},
    };

    // 判断type的取值
    if (!(params.type == "get" || params.type == "post")) {
        throw new Error("请求方式暂时只支持get 或者post");
    }

    // 判断data参数是否是字符串或者对象
    // Object.prototype.toString.call('123') == '[object String]'
    let data = Object.prototype.toString.call(params.data);
    if (!(data == "[object String]" || data == "[object Object]")) {
        throw new Error("data参数必须是字符串或者对象");
    }

    // 如果你参数是字符串类型 必须有 =
    // key=value
    if (
        params.data &&
        data == "[object String]" &&
        !params.data.includes("=")
    ) {
        throw new Error("data参数为字符串的时候 只能是 key=value");
    }

    // 判断success 是否是一个函数
    if (Object.prototype.toString.call(params.success) != "[object Function]") {
        throw new Error("success 必须是一个函数");
    }

    /* 
        如果data是一个对象的是 {username:123,age:18}
        url?username=123&age=18
        send(username=123&age=18)
    */
    if (data == "[object Object]") {
        // 说明data是一个对象
        // 那么就需要把对象转换为字符串 key=value
        // {username:123,age:18}===>username=123&age=18
        let str = "";
        for (let key in params.data) {
            str += key + "=" + params.data[key] + "&";
        }
        // 把最后一个& 符号截取掉
        // 把截取的结果赋值给params.data
        params.data = str.slice(0, -1);
    }

    // 创建ajax对象
    let xhr = new XMLHttpRequest();

    /* 
        如果是get请求 那么直接把参数拼接url地址后面
        如果是post请求 那么把参数写在send() 并且设置请求头

    */
    if (params.type == "get") {
        xhr.open(params.type, params.url + "?" + params.data, params.async);
        xhr.send();
    } else {
        xhr.open(params.type, params.url, params.async);
        xhr.setRequestHeader(
            "content-type",
            "application/x-www-form-urlencoded"
        );
        xhr.send(params.data);
    }
    /* 
        同步请求的时候 不执行load 事件
    */
    /* 
        http请求的状态码
        ajax 本来就是用于发送异步的http请求的一门技术
        http请求的状态码为3位数字
        当http请求状态码为 1开头 表示继续请求中
        当http请求状态码为 2开头 表示请求成功
        当http请求状态码为 3开头 表示请求成功 但是这个数据是从缓存重获取
        当http请求状态码为 4开头 请求失败 且一些错误都是客户端的错误（前端的错误）
        当http请求状态码为 5开头 表示失败 这个错误都是服务端（后端）的错误

        ajax对象中 xhr.status 用来存储http请求的状态码

        怎么判断请求失败呢 当http请求状态码不为 2 或者3 开头 就表示请求失败
    */

    if (params.async == false) {
        // test() 返回值 布尔值 如果满足正则就返回true 否则就返回false
        if (/^[23]\d{2}$/.test(xhr.status)) {
            params.success(xhr.responseText);
            return;
        }
        params.error(xhr.statusText);

        return;
    }

    // 异步请求获取结果的位置
    xhr.onload = function () {
        //判断http请求的状态码
        if (/^[23]\d{2}$/.test(xhr.status)) {
            params.success(xhr.responseText);
            return;
        }
        params.error(xhr.statusText);
    };
}

/* 
    二次封装： 把已经封装过多函数再次进行封装
    功能：主要就是把promise对象封装起来
*/
function pAjax(option) {
    let p = new Promise((resolve, reject) => {
        ajax({
            url: option.url,
            type: option.type,
            data: option.data,
            async: option.async,
            // ajax请求成功 执行resolve 表示promise执行就是成功
            success: function (res) {
                resolve(res);
            },

            // ajax请求失败的时候执行的函数
            // 当ajax请求失败 那么promise执行就是失败
            error: function (err) {
                reject(err);
            },
        });
    });
    return p;
}
